home *** CD-ROM | disk | FTP | other *** search
/ NetNews Offline 2 / NetNews Offline Volume 2.iso / news / comp / lang / c++-part1 / 1942 < prev    next >
Encoding:
Text File  |  1996-08-06  |  3.9 KB  |  146 lines

  1. Path: news.th-darmstadt.de!news!enno
  2. From: enno@inferenzsysteme.informatik.th-darmstadt.de (Enno Sandner)
  3. Newsgroups: comp.lang.c++
  4. Subject: Re: Typecasting
  5. Date: 14 Jan 1996 10:19:55 GMT
  6. Organization: Fachbereich Informatik, TH Darmstadt
  7. Distribution: world
  8. Message-ID: <ENNO.96Jan14111955@kitz.inferenzsysteme.informatik.th-darmstadt.de>
  9. References: <30F763B0.319@dmr.ca>
  10. NNTP-Posting-Host: kitz.intellektik.informatik.th-darmstadt.de
  11. In-reply-to: Bernard Drolet's message of Fri, 12 Jan 1996 23:48:00 -0800
  12.  
  13. In article <30F763B0.319@dmr.ca> Bernard Drolet <Bernard.Drolet@dmr.ca> writes:
  14.  
  15.    I'm writing a program using the Doc-View method.
  16.  
  17.    Presently, the main classes are (simplified)
  18.  
  19.    class Base
  20.    {
  21.       int GetType() = 0;
  22.       // some other methods and variables
  23.    };
  24.  
  25.    class A : public Base
  26.    {
  27.       int GetType() { return TypeA; }
  28.       // methods and variables
  29.    };
  30.  
  31.    class B : public Base
  32.    {
  33.       int GetType() { return TypeB; }
  34.       // methods and variables
  35.    };
  36.  
  37.    class Doc
  38.    {
  39.       List<Base> ListOfBaseObjects;
  40.       Doc();
  41.    };
  42.  
  43.    class View
  44.    {
  45.       Doc CurrentDocument;
  46.       void Paint();    
  47.       void PaintAObject();
  48.       void PaintBObject();
  49.    };
  50.  
  51.    void Doc::Doc()
  52.    {
  53.       A* a = new A();
  54.       B* b = new B();
  55.  
  56.       ListOfBaseObjects.Add(a);
  57.       ListOfBaseObjects.Add(b);
  58.    }
  59.  
  60.    void View::Paint()
  61.    {
  62.       Iterator<CurrentDocument.ListOfBaseObjects> ObjectsIterator;
  63.  
  64.       while ( ObjectsIterator )
  65.       {
  66.      switch ( ObjectsIterator.Current()->GetType() )
  67.      {
  68.         case TypeA:
  69.            PaintAObject();
  70.            break;
  71.         case TypeB:
  72.            PaintBObject();
  73.            break;
  74.      }
  75.      ObjectsIterator++;
  76.       }
  77.    }
  78.  
  79.  
  80.    What I would like better is to remove the switch statement in the paint() function and 
  81.    find a way to define functions looking like Paint(A& a), Paint(B& b) and to call them 
  82.    directly, but is there a way to discover what type an object that was inserted in the list 
  83.    is?
  84.  
  85.    I would like to replace the switch in the paint() with a statement like
  86.       while ( ObjectsIterator )
  87.       {
  88.      Paint(ObjectsIterator.Current())
  89.      ...
  90.  
  91.    So can I typecast the abstract Base object contained in the list to its actual type ?
  92.  
  93. RTTI could help you to determine the dynamic-type of an instance. However
  94. this solution would lead to sth. similar to your solution (according to the
  95. drawbacks). A technique for adding type-specific behavior is the 'visitor'
  96. pattern. You have to augment the interface of your classes, add the 'Visitor'
  97. class and a subclass of 'Visitor' that implements the type-specific behavior.
  98. The 'Visitor' class contains a function for each possible type of your class
  99. hierarchie:
  100.  
  101.             class Visitor {
  102.                      public:
  103.               virtual ~Visitor() {}
  104.               virtual void VisitA(A&) {}
  105.                           virtual void VisitB(B&) {}
  106.                     };
  107.  
  108. in this example the suitable subclass 'ViewVisitor' takes a reference to
  109. a 'View' and performs the appropriate operations.
  110.  
  111.         class ViewVisitor : public Visitor {
  112.          public:
  113.           ViewVisitor(View& view) : view_(view) {}
  114.                   void VisitA(A&) { view_.PaintAObject(); }
  115.                   void VisitB(B&) { view_.PaintBObject(); }
  116.                  private:
  117.                   View& view_;
  118.                 };
  119.  
  120. For other tasks, you simply create a new subclass.
  121. Now the interface of 'Base' is enriched with the 'void Accept(Visitor&)'
  122. function, which is implemented in each subclass by simply calling back
  123. the 'Visitor' with the appropriate type, ie:
  124.     
  125.     void A :: Accept(Visitor& v) { v.VisitA(*this); }
  126.     void B :: Accept(Visitor& v) { v.VisitB(*this); }
  127.  
  128. The resulting 'View :: paint' writes as:
  129.  
  130. void View::Paint()
  131. {
  132.    Iterator<CurrentDocument.ListOfBaseObjects> ObjectsIterator;
  133.    ViewVisitor vview(*this);
  134.  
  135.    while ( ObjectsIterator )
  136.    {
  137.       ObjectsIterator.Current()->Accept(vview);
  138.       ObjectsIterator++;
  139.    }
  140. }
  141.  
  142. This solution makes it easy to 'plug-in' new behavior, but adding new classes
  143. enforce a modification of the 'Visitor' interface.
  144.  
  145.     Enno
  146.